﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_GL_STATE_MANAGER_H
#define _UXS_GL_STATE_MANAGER_H

#pragma once

#include "GLStatistic.h"
#include "GLRenderState.h"

namespace UX_STL
{
namespace GLShell
{

#ifdef UX_SMAN_DISABLE
#	define	SMAN_CHECK_STATE( _check )
#else
#	define	SMAN_CHECK_STATE( _check )	_check
#endif
	

	namespace e_gl_state
	{
		enum	type	: uint8
		{
			COLOR_LOGIC_OP		= 0,
			CULL_FACE,
			DEPTH_CLAMP,
			DEPTH_TEST,
			DITHER,
			LINE_SMOOTH,
			MULTISAMPLE,
			POLYGON_OFFSET_FILL,
			POLYGON_OFFSET_LINE,
			POLYGON_OFFSET_POINT,
			POLYGON_SMOOTH,
			PRIMITIVE_RESTART,
			SAMPLE_ALPHA_TO_COVERAGE,
			SAMPLE_ALPHA_TO_ONE,
			SAMPLE_COVERAGE,
			SCISSOR_TEST,
			STENCIL_TEST,
			TEXTURE_CUBE_MAP_SEAMLESS,
			PROGRAM_POINT_SIZE,
			RASTERIZER_DISCARD,
			FRAMEBUFFER_SRGB
		};
	}


	struct TSMGLState
	{
		GLenum	uGLEnum;
		bool	bEnabled;
		///
		TSMGLState(): uGLEnum(0), bEnabled(false) {}
		TSMGLState(GLenum type, bool enabled): uGLEnum(type), bEnabled(enabled) {}
	};



	//
	// State Manager
	//

	class UX_EXPORT GLStateManager : public GLBaseObject
	{
		friend class GLSystem;

	private:
		struct TViewport
		{
			irect						sArea;
			static_stack< irect, 16 >	sScissorStack;
			///
			TViewport() {}
		};
		
		typedef static_stack< TViewport, 16 >	viewport_stack_t;
		typedef static_stack< GLTexLayer, 16 >	texture_stack_t;
		typedef static_stack< GLuint, 8 >		gl_id_stack_t;
		typedef GLRenderState::TColorBuffer		TColorBuffer;
		typedef GLRenderState::TStencilState	TStencilState;
		
	private:
		GLStatistic				_cStatistic;

		array< TSMGLState >		_aStates;

		// Textures //
		array< GLTexLayerEx >	_aTexLayers;
		uint					_uCurLayer;
		texture_stack_t			_sTexStack;

		// Blend //
		static_array< TColorBuffer, UX_GL_MAX_DRAW_BUFFERS >
								_aColorBuffers;

		// Depth //
		gl_compare::type		_eDepthFunc;
		bool					_bDepthMask;

		// Stencil //
		TStencilState			_sStencil;

		// GLSL //
		GLuint					_uProgram,
								_uProgramPipeline;

		// Cull Fase //
		gl_face::type			_eCullMode;

		// Polygon Mode //
		gl_polygon_mode::type	_ePolygonMode;

		// Front Face Mode //
		gl_front_face::type		_eFrontFace;

		// Buffers //
		GLuint					_uVAO,
								_uIBO,
								_uRBO,
								_uReadFBO,
								_uDrawFBO;

		gl_id_stack_t			_sReadFBOStack,
								_sDrawFBOStack,
								_sVAOStack;

		// Transform Feedback //
		GLuint					_uTransform;

		// Viewport //
		viewport_stack_t		_sViewportStack;

		// Tessellation //
		uint					_uPatchSize;
		bool					_bTessEnable	: 1;

		// Locks //
		bool					_bLockTexture	: 1,
								_bLockSampler	: 1,
								_bLockProgram	: 1,
								_bLockVAO		: 1,
								_bLockFBO		: 1;

	private:
		bool  _Init();

	public:
		GLStateManager();
		~GLStateManager();

		void	Enable   (e_gl_state::type	eType);
		void	Disable  (e_gl_state::type	eType);
		bool	IsEnabled(e_gl_state::type	eType)	const;
		GLenum	GetGLType(e_gl_state::type	eType)	const;
		void	SetState (e_gl_state::type	eType, bool bState);

		void ActiveTexture(uint stage);
		void BindFramebuffer(GLuint uFBO);
		void BindReadFramebuffer(GLuint uFBO);
		void BindDrawFramebuffer(GLuint uFBO);
		void BindRenderbuffer(GLuint uRBO);
		void BindMultiTexture(uint stage, gl_texture::type target, GLuint texture);
		void BindProgramPipeline(GLuint pipeline);
		void BindSampler(GLuint unit, GLuint sampler);
		void BindTexture(gl_texture::type target, GLuint texture);
		void BindBuffer(gl_buffer::type target, GLuint id);
		void BindIndexBuffer(GLuint uIBO);
		void BindVertexArray(GLuint uVAO);
		void BindTransformFeedback(GLuint id);
		void BlendState(GLuint buf, bool bBlending);
		void BlendEquation(gl_blend_equation::type mode);
		void BlendEquationi(GLuint buf, gl_blend_equation::type mode);
		void BlendEquationSeparate(gl_blend_equation::type modeRGB, gl_blend_equation::type modeAlpha);
		void BlendEquationSeparatei(GLuint buf, gl_blend_equation::type modeRGB, gl_blend_equation::type modeAlpha);
		void BlendFunc(gl_blend_func::type sfactor, gl_blend_func::type dfactor);
		void BlendFunci(GLuint buf, gl_blend_func::type sfactor, gl_blend_func::type dfactor);
		void BlendFuncSeparate(gl_blend_func::type srcRGB, gl_blend_func::type dstRGB, gl_blend_func::type srcAlpha, gl_blend_func::type dstAlpha);
		void BlendFuncSeparatei(GLuint buf, gl_blend_func::type srcRGB, gl_blend_func::type dstRGB, gl_blend_func::type srcAlpha, gl_blend_func::type dstAlpha);
		void CullFace(gl_face::type mode);
		void ColorMask(bool red, bool green, bool blue, bool alpha);
		void ColorMaski(GLuint buf, bool red, bool green, bool blue, bool alpha);
		void ColorMaski(GLuint buf, e_color_mask::type eMask);
		void DepthMask(bool mode);
		void DepthFunc(gl_compare::type func);
		void StencilFunc(gl_compare::type func, GLint ref, GLuint mask);
		void StencilFuncSeparate(gl_face::type face, gl_compare::type func, GLint ref, GLuint mask);
		void StencilMask(GLuint mask);
		void StencilMaskSeparate(gl_face::type face, GLuint mask);
		void StencilOp(gl_stencil_op::type sfail, gl_stencil_op::type dfail, gl_stencil_op::type dppass);
		void StencilOpSeparate(gl_face::type face, gl_stencil_op::type sfail, gl_stencil_op::type dfail, gl_stencil_op::type dppass);
		void FrontFace(gl_front_face::type mode);
		void PolygonMode(gl_polygon_mode::type mode);
		void UseProgram(GLuint uProgram);

		void Viewport(const irect &sArea);			// |_
		void Viewport(const ivec2 &vSize);
		void PopViewport();
		void PushViewport()							{ _sViewportStack.Push(); ClearScissorStack(); }
		const irect & GetViewport()			const	{ return _sViewportStack.Get().sArea; }

		void Scissor(const irect &sArea, bool bDepend = false);		// |_
		void Scissor(const frect &sArea, bool bDepend = false);		// 0..1	|_
		void ClearScissorStack();
		void PopScissor();
		void PushScissor()							{ _sViewportStack.Get().sScissorStack.Push(); }
		const irect & GetScissor()			const	{ return _sViewportStack.Get().sScissorStack.Get(); }

		bool PushCurrentTexture();
		bool PopCurrentTexture();
		bool PushDrawFramebuffer();
		bool PopDrawFramebuffer();
		bool PushReadFramebuffer();
		bool PopReadFramebuffer();
		bool PushVertexArray();
		bool PopVertexArray();

		GLuint	GetTransformFeedback()		const	{ return _uTransform; }
		GLuint	GetIndexBuffer()			const	{ return _uIBO; }
		GLuint	GetReadFBO()				const	{ return _uReadFBO; }
		GLuint	GetDrawFBO()				const	{ return _uDrawFBO; }
		GLuint	GetRBO()					const	{ return _uRBO; }
		GLuint	GetVAO()					const	{ return _uVAO; }
		GLuint	GetProgram()				const	{ return _uProgram; }
		uint	GetCurrentLayer()			const	{ return _uCurLayer; }
		GLuint	GetCurrentTexture()			const	{ return _aTexLayers[_uCurLayer].uID; }
		GLenum	GetCurrentTexTarget()		const	{ return _aTexLayers[_uCurLayer].eTarget; }
		GLuint	GetTexture(uint i)			const	{ return _aTexLayers[i].uID; }
		GLenum	GetTexTarget(uint i)		const	{ return _aTexLayers[i].eTarget; }
		GLuint	GetSampler(uint i)			const	{ return _aTexLayers[i].uSampler; }
		bool	GetDepthMask()				const	{ return _bDepthMask; }
		
		e_color_mask::type	GetColorMask()	const;

		GLStatistic * GetStatistic()				{ return &_cStatistic; }

		e_gl_state::type AddGLState(const TSMGLState &sState);
		void SetGLState(const TSMGLState &sState, e_gl_state::type eType);

		void Refresh();
		void UnbindTextures();
		void UnbindSamplers();
		void UnbindTexture(uint8 uStage);
		void UnbindTexSampler(uint8 uStage);

		void LockTextures(bool bLock)				{ _bLockTexture = bLock; }
		void LockSamplers(bool bLock)				{ _bLockSampler = bLock; }
		void LockProgram(bool bLock)				{ _bLockProgram = bLock; }
		void LockArrayBuffer(bool bLock)			{ _bLockVAO = bLock; }
		void LockFrameBuffer(bool bLock)			{ _bLockFBO = bLock; }
		

		void EnableTessellation(bool bEnable, uint uPatchSize);
		
		void DrawArrays(gl_primitive::type mode, GLint first, GLsizei count);
		void DrawArraysIndirect(gl_primitive::type mode, const GLDrawArraysIndirectCommand * indirect);
		void DrawArraysInstanced(gl_primitive::type mode, GLint first, GLsizei count, GLsizei primcount);
		void DrawArraysInstancedBaseInstance(gl_primitive::type mode, GLint first, GLsizei count, GLsizei primcount, GLuint baseinstance);
		void DrawElements(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices);
		void DrawElementsBaseVertex(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices, GLint basevertex);
		void DrawElementsIndirect(gl_primitive::type mode, gl_index::type type, const GLDrawElementsIndirectCommand * indirect);
		void DrawElementsInstanced(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices, GLsizei primcount);
		void DrawElementsInstancedBaseInstance(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid *indices, GLsizei primcount, GLuint baseinstance);
		void DrawElementsInstancedBaseVertex(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices, GLsizei primcount, GLint basevertex);
		void DrawElementsInstancedBaseVertexBaseInstance(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid *indices, GLsizei primcount, GLint basevertex, GLuint baseinstance);
		void DrawRangeElements(gl_primitive::type mode, GLuint start, GLuint end, GLsizei count, gl_index::type type, const GLvoid * indices);
		void DrawRangeElementsBaseVertex(gl_primitive::type mode, GLuint start, GLuint end, GLsizei count, gl_index::type type, const GLvoid * indices, GLint basevertex);
		void DrawTransformFeedback(gl_primitive::type mode, GLuint id);
		void DrawTransformFeedbackInstanced(gl_primitive::type mode, GLuint id, GLsizei primcount);
		void DrawTransformFeedbackStream(gl_primitive::type mode, GLuint id, GLuint stream);
		void DrawTransformFeedbackStreamInstanced(gl_primitive::type mode, GLuint id, GLuint stream, GLsizei primcount);
		void MultiDrawArrays(gl_primitive::type mode, const GLint * first, const GLsizei * count, GLsizei primcount);
		void MultiDrawElements(gl_primitive::type mode, const GLsizei * count, gl_index::type type, const GLvoid ** indices, GLsizei primcount);
		void MultiDrawElementsBaseVertex(gl_primitive::type mode, const GLsizei * count, gl_index::type type, const GLvoid ** indices, GLsizei primcount, const GLint * basevertex);
		void MultiDrawArraysIndirect(gl_primitive::type mode, const GLDrawArraysIndirectCommand *indirect, GLsizei drawcount, GLsizei stride);
		void MultiDrawElementsIndirect(gl_primitive::type mode, gl_index::type type, const GLDrawElementsIndirectCommand *indirect, GLsizei drawcount, GLsizei stride);
	};

/*
=================================================
	активирует состояние
=================================================
*/
	inline void GLStateManager::Enable(e_gl_state::type eType)
	{
		SMAN_CHECK_STATE
		(
		if ( _aStates[eType].bEnabled ) {
			GL_COUNTER( ++_cStatistic.uStateResets; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uStateSets; );

		GL_CALL( glEnable( _aStates[eType].uGLEnum ) );
		_aStates[eType].bEnabled = true;
	}

/*
=================================================
	деактивирует состояние
=================================================
*/
	inline void GLStateManager::Disable(e_gl_state::type eType)
	{
		SMAN_CHECK_STATE
		(
		if ( !_aStates[eType].bEnabled ) {
			GL_COUNTER( ++_cStatistic.uStateResets; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uStateSets; );

		GL_CALL( glDisable( _aStates[eType].uGLEnum ) );
		_aStates[eType].bEnabled = false;
	}

/*
=================================================
	проверяет активированно ли
=================================================
*/
	inline bool GLStateManager::IsEnabled(e_gl_state::type eType) const
	{
		return _aStates[eType].bEnabled;
	}

/*
=================================================
	переводит тип менеджера в OpenGL тип
=================================================
*/
	inline GLenum GLStateManager::GetGLType(e_gl_state::type eType) const
	{
		return _aStates[eType].uGLEnum;
	}

/*
=================================================
	устанавливает состояние
=================================================
*/
	inline void GLStateManager::SetState(e_gl_state::type eType, bool bState)
	{
		SMAN_CHECK_STATE
		(
		if ( _aStates[eType].bEnabled == bState ) {
			GL_COUNTER( ++_cStatistic.uStateResets; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uStateSets; );

		_aStates[eType].bEnabled = bState;

		if ( bState )	{ GL_CALL( glEnable(  _aStates[eType].uGLEnum ) ); }
		else			{ GL_CALL( glDisable( _aStates[eType].uGLEnum ) ); }
	}

/*
=================================================
	активирует текстурный слой
=================================================
*/
	inline void GLStateManager::ActiveTexture(uint stage)
	{
		SMAN_CHECK_STATE
		(
		if ( stage == _uCurLayer )
			return;
		);
		GL_CALL( glActiveTexture( GL_TEXTURE0 + stage ) );
		_uCurLayer = stage;
	}

/*
=================================================
	устанавливает режим смешивания
=================================================
*/
	inline void GLStateManager::BlendState(GLuint buf, bool bBlending)
	{
		SMAN_CHECK_STATE
		(
		if ( _aColorBuffers[buf].bBlending == bBlending )
			return;
		)
		GL_CALL( bBlending ? glEnablei( gl_state::BLEND, buf ) :
							 glDisablei( gl_state::BLEND, buf ) );
		_aColorBuffers[buf].bBlending = bBlending;
	}

	inline void GLStateManager::BlendEquation(gl_blend_equation::type mode)
	{
		GL_CALL( glBlendEquation( mode ) );
		FOR( i, _aColorBuffers ) {
			_aColorBuffers[i].eModeAlpha = _aColorBuffers[i].eModeRGB = mode;
		}
	}

	inline void GLStateManager::BlendEquationi(GLuint buf, gl_blend_equation::type mode)
	{
		SMAN_CHECK_STATE
		(
		if ( _aColorBuffers[buf].eModeRGB == mode and _aColorBuffers[buf].eModeAlpha == mode )
			return;
		)
		GL_CALL( glBlendEquationi( buf, mode ) );
		_aColorBuffers[buf].eModeAlpha = _aColorBuffers[buf].eModeRGB = mode;
	}

	inline void GLStateManager::BlendEquationSeparate(gl_blend_equation::type modeRGB, gl_blend_equation::type modeAlpha)
	{
		GL_CALL( glBlendEquationSeparate( modeRGB, modeAlpha ) );
		FOR( i, _aColorBuffers ) {
			_aColorBuffers[i].eModeRGB	 = modeRGB;
			_aColorBuffers[i].eModeAlpha = modeAlpha;
		}
	}

	inline void GLStateManager::BlendEquationSeparatei(GLuint buf, gl_blend_equation::type modeRGB, gl_blend_equation::type modeAlpha)
	{
		SMAN_CHECK_STATE
		(
		if ( _aColorBuffers[buf].eModeRGB == modeRGB and _aColorBuffers[buf].eModeAlpha == modeAlpha )
			return;
		);
		GL_CALL( glBlendEquationSeparatei( buf, modeRGB, modeAlpha ) );
		_aColorBuffers[buf].eModeRGB	= modeRGB;
		_aColorBuffers[buf].eModeAlpha	= modeAlpha;
	}

/*
=================================================
	устанавливает функцию смешивания
=================================================
*/
	inline void GLStateManager::BlendFunc(gl_blend_func::type sfactor, gl_blend_func::type dfactor)
	{
		GL_CALL( glBlendFunc( sfactor, dfactor ) );
		FOR( i, _aColorBuffers ) {
			_aColorBuffers[i].eSrcRGB = _aColorBuffers[i].eSrcAlpha = sfactor;
			_aColorBuffers[i].eDstRGB = _aColorBuffers[i].eDstAlpha = dfactor;
		}
	}
	
	inline void GLStateManager::BlendFunci(GLuint buf, gl_blend_func::type sfactor, gl_blend_func::type dfactor)
	{
		SMAN_CHECK_STATE
		(
		if ( _aColorBuffers[buf].eSrcRGB == sfactor and _aColorBuffers[buf].eSrcAlpha == sfactor and
			 _aColorBuffers[buf].eDstRGB == dfactor and _aColorBuffers[buf].eDstAlpha == dfactor )
			return;
		);

		GL_CALL( glBlendFunci( buf, sfactor, dfactor ) );
		_aColorBuffers[buf].eSrcRGB = _aColorBuffers[buf].eSrcAlpha = sfactor;
		_aColorBuffers[buf].eDstRGB = _aColorBuffers[buf].eDstAlpha = dfactor;
	}

	inline void GLStateManager::BlendFuncSeparate(gl_blend_func::type srcRGB, gl_blend_func::type dstRGB,
												 gl_blend_func::type srcAlpha, gl_blend_func::type dstAlpha)
	{
		GL_CALL( glBlendFuncSeparate( srcRGB, dstRGB, srcAlpha, dstAlpha ) );
		FOR( i, _aColorBuffers ) {
			_aColorBuffers[i].eSrcAlpha = srcAlpha;		_aColorBuffers[i].eSrcRGB = srcRGB;
			_aColorBuffers[i].eDstAlpha = dstAlpha;		_aColorBuffers[i].eDstRGB = dstRGB;
		}
	}
	
	inline void GLStateManager::BlendFuncSeparatei(GLuint buf, gl_blend_func::type srcRGB, gl_blend_func::type dstRGB,
												  gl_blend_func::type srcAlpha, gl_blend_func::type dstAlpha)
	{
		SMAN_CHECK_STATE
		(
		if ( _aColorBuffers[buf].eSrcRGB == srcRGB and _aColorBuffers[buf].eSrcAlpha == srcAlpha and
			 _aColorBuffers[buf].eDstRGB == dstRGB and _aColorBuffers[buf].eDstAlpha == dstAlpha )
			 return;
		);

		GL_CALL( glBlendFuncSeparatei( buf, srcRGB, dstRGB, srcAlpha, dstAlpha ) );
		_aColorBuffers[buf].eSrcAlpha = srcAlpha;		_aColorBuffers[buf].eSrcRGB = srcRGB;
		_aColorBuffers[buf].eDstAlpha = dstAlpha;		_aColorBuffers[buf].eDstRGB = dstRGB;
	}

/*
=================================================
	устанавливает кадровый буфер
=================================================
*/
	inline void GLStateManager::BindFramebuffer(GLuint uFBO)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockFBO or (_uReadFBO == uFBO and _uDrawFBO == uFBO) ) {
			GL_COUNTER( ++_cStatistic.uFBORebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uFBOBind; );

		GL_CALL( glBindFramebuffer( GL_FRAMEBUFFER, uFBO ) );
		_uReadFBO = _uDrawFBO = uFBO;
	}


	inline void GLStateManager::BindReadFramebuffer(GLuint uFBO)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockFBO or _uReadFBO == uFBO ) {
			GL_COUNTER( ++_cStatistic.uFBORebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uFBOBind; );

		GL_CALL( glBindFramebuffer( GL_READ_FRAMEBUFFER, uFBO ) );
		_uReadFBO = uFBO;
	}


	inline void GLStateManager::BindDrawFramebuffer(GLuint uFBO)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockFBO or _uDrawFBO == uFBO ) {
			GL_COUNTER( ++_cStatistic.uFBORebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uFBOBind; );

		GL_CALL( glBindFramebuffer( GL_DRAW_FRAMEBUFFER, uFBO ) );
		_uDrawFBO = uFBO;
	}
	
/*
=================================================
	устанавливает кадровый буфер
=================================================
*/
	inline void GLStateManager::BindRenderbuffer(GLuint uRBO)
	{
		SMAN_CHECK_STATE
		(
		if ( _uRBO == uRBO ) {
			GL_COUNTER( ++_cStatistic.uRBORebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uRBOBind; );

		GL_CALL( glBindRenderbuffer( GL_RENDERBUFFER, uRBO ) );
		_uRBO = uRBO;
	}

/*
=================================================
	устанавливает текстуру
=================================================
*/
	inline void GLStateManager::BindMultiTexture(uint stage, gl_texture::type target, GLuint texture)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockTexture or (
			_aTexLayers[stage].eTarget == target and
			_aTexLayers[stage].uID == texture) )
		{
			GL_COUNTER( ++_cStatistic.uTextureRebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uTextureBind; );

		GL_CALL( glBindMultiTexture( GL_TEXTURE0 + stage, target, texture ) );
		_aTexLayers[stage].eTarget = target;
		_aTexLayers[stage].uID  = texture;
	}

/*
=================================================
	устанавливает текстуру
=================================================
*/
	inline void GLStateManager::BindTexture(gl_texture::type target, GLuint texture)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockTexture or (
			 _aTexLayers[_uCurLayer].eTarget == target and
			 _aTexLayers[_uCurLayer].uID == texture ) )
		{
			GL_COUNTER( ++_cStatistic.uTextureRebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uTextureBind; );

		GL_CALL( glBindTexture( target, texture ) );
		_aTexLayers[_uCurLayer].eTarget = target;
		_aTexLayers[_uCurLayer].uID  = texture;
	}
	
/*
=================================================

=================================================
*/
	inline void GLStateManager::BindTransformFeedback(GLuint id)
	{
		SMAN_CHECK_STATE
		(
		if ( id == _uTransform ) {
			GL_COUNTER( ++_cStatistic.uTFRebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uTFBind; );

		GL_CALL( glBindTransformFeedback( GL_TRANSFORM_FEEDBACK, id ) );
		_uTransform = id;
	}

/*
=================================================
	устанавливает шейдерный конвеер
=================================================
*/
	inline void GLStateManager::BindProgramPipeline(GLuint pipeline)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockProgram or _uProgramPipeline == pipeline ) {
			GL_COUNTER( ++_cStatistic.uPipelineRebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uPipelineBind; );

		GL_CALL( glBindProgramPipeline( pipeline ) );
		_uProgramPipeline = pipeline;
	}

/*
=================================================
	устанавливает сэмплер
=================================================
*/
	inline void GLStateManager::BindSampler(GLuint unit, GLuint sampler)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockSampler or _aTexLayers[unit].uSampler == sampler ) {
			GL_COUNTER( ++_cStatistic.uSamplerRebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uSamplerBind; );

		GL_CALL( glBindSampler( unit, sampler ) );
		_aTexLayers[unit].uSampler = sampler;
	}
	
/*
=================================================
	
=================================================
*/
	inline void GLStateManager::BindBuffer(gl_buffer::type target, GLuint id)
	{
		if ( target == gl_buffer::INDEX )
		{
			if ( id != _uIBO )	_uIBO = id;
			else				return;
		}
		GL_CALL( glBindBuffer( target, id ) );
	}
	
/*
=================================================
	
=================================================
*/
	inline void GLStateManager::BindIndexBuffer(GLuint uIBO)
	{
		SMAN_CHECK_STATE
		(
		if ( _uIBO == uIBO )
			return;
		)

		GL_CALL( glBindBuffer( gl_buffer::INDEX, uIBO ) );
		_uIBO = uIBO;
	}

/*
=================================================
	устанавливает текущий массив вершин
=================================================
*/
	inline void GLStateManager::BindVertexArray(GLuint uVAO)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockVAO or uVAO == _uVAO ) {
			GL_COUNTER( ++_cStatistic.uVAORebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uVAOBind; );

		GL_CALL( glBindVertexArray( uVAO ) );
		_uVAO = uVAO;
		_uIBO = 0;	// reset index buffer
	}

/*
=================================================
	изменяет режин записи в буфер глубины
=================================================
*/
	inline void GLStateManager::DepthMask(bool mode)
	{
		SMAN_CHECK_STATE
		(
		if ( _bDepthMask == mode )
			return;
		);

		GL_CALL( glDepthMask( mode ) );
		_bDepthMask = mode;
	}

/*
=================================================
	изменяет режин записи в буфер глубины
=================================================
*/
	inline void GLStateManager::DepthFunc(gl_compare::type func)
	{
		SMAN_CHECK_STATE
		(
		if ( _eDepthFunc == func )
			return;
		);

		GL_CALL( glDepthFunc( func ) );
		_eDepthFunc = func;
	}

/*
=================================================
	изменяет режим отсечения полигонов
=================================================
*/
	inline void GLStateManager::CullFace(gl_face::type mode)
	{
		SMAN_CHECK_STATE
		(
		if ( _eCullMode == mode )
			return;
		);

		GL_CALL( glCullFace( mode ) );
		_eCullMode = mode;
	}
	
/*
=================================================
	устанавливает фильтр для цветов
=================================================
*/
	inline void GLStateManager::ColorMask(bool red, bool green, bool blue, bool alpha)
	{
		const e_color_mask::type	mask = e_color_mask::type( (red << 0) | (green << 1) | (blue << 2) | (alpha << 3) );

		GL_CALL( glColorMask( red, green, blue, alpha ) );
		FOR( i, _aColorBuffers ) {
			_aColorBuffers[i].eMask = mask;
		}
	}
	
	inline void GLStateManager::ColorMaski(GLuint buf, bool red, bool green, bool blue, bool alpha)
	{
		const e_color_mask::type	mask = e_color_mask::type( (red << 0) | (green << 1) | (blue << 2) | (alpha << 3) );

		SMAN_CHECK_STATE
		(
			if ( _aColorBuffers[buf].eMask == mask )
				return;
		)

		GL_CALL( glColorMaski( buf, red, green, blue, alpha ) );
		_aColorBuffers[buf].eMask = mask;
	}

	inline void GLStateManager::ColorMaski(GLuint buf, e_color_mask::type eMask)
	{
		ColorMaski( buf, EnumCmp( eMask, e_color_mask::R ),
						 EnumCmp( eMask, e_color_mask::G ),
						 EnumCmp( eMask, e_color_mask::B ),
						 EnumCmp( eMask, e_color_mask::A ) );
	}

/*
=================================================
	устанавливает направление обхода вершин
=================================================
*/
	inline void GLStateManager::FrontFace(gl_front_face::type mode)
	{
		SMAN_CHECK_STATE
		(
		if ( _eFrontFace == mode )
			return;
		);

		GL_CALL( glFrontFace( mode ) );
		_eFrontFace = mode;
	}

/*
=================================================
	устанавливает режим стенсил теста
=================================================
*/
	inline void GLStateManager::StencilFunc(gl_compare::type func, GLint ref, GLuint mask)
	{
		SMAN_CHECK_STATE
		(
		if ( _sStencil.aSide[0].eFunc == func and _sStencil.aSide[0].uFuncRef == ref and _sStencil.aSide[0].uFuncMask == mask and
			 _sStencil.aSide[1].eFunc == func and _sStencil.aSide[1].uFuncRef == ref and _sStencil.aSide[1].uFuncMask == mask )
			return;
		);

		GL_CALL( glStencilFunc( func, ref, mask ) );
		_sStencil.aSide[0].eFunc		= _sStencil.aSide[1].eFunc		= func;
		_sStencil.aSide[0].uFuncRef		= _sStencil.aSide[1].uFuncRef	= ref;
		_sStencil.aSide[0].uFuncMask	= _sStencil.aSide[1].uFuncMask	= mask;
	}

	inline void GLStateManager::StencilFuncSeparate(gl_face::type face, gl_compare::type func, GLint ref, GLuint mask)
	{
		const uint	i = face == gl_face::FRONT;

		SMAN_CHECK_STATE
		(
		if ( _sStencil.aSide[i].eFunc == func and _sStencil.aSide[i].uFuncRef == ref and _sStencil.aSide[i].uFuncMask == mask )
			return;
		);

		GL_CALL( glStencilFuncSeparate( face, func, ref, mask ) );
		_sStencil.aSide[i].eFunc	 = func;
		_sStencil.aSide[i].uFuncRef	 = ref;
		_sStencil.aSide[i].uFuncMask = mask;
	}

/*
=================================================
	устанавливает маску стенсила
=================================================
*/
	inline void GLStateManager::StencilMask(GLuint mask)
	{
		SMAN_CHECK_STATE
		(
		if ( _sStencil.aSide[0].uMask == mask and _sStencil.aSide[1].uMask == mask )
			return;
		);

		GL_CALL( glStencilMask( mask ) );
		_sStencil.aSide[0].uMask = _sStencil.aSide[1].uMask = mask;
	}

	inline void GLStateManager::StencilMaskSeparate(gl_face::type face, GLuint mask)
	{
		const uint	i = face == gl_face::FRONT;

		SMAN_CHECK_STATE
		(
		if ( _sStencil.aSide[i].uMask == mask )
			return;
		);

		GL_CALL( glStencilMaskSeparate( face, mask ) );
		_sStencil.aSide[i].uMask = mask;
	}
	
/*
=================================================
	устанавливает операции стенсила
=================================================
*/
	inline void GLStateManager::StencilOp(gl_stencil_op::type sfail, gl_stencil_op::type dfail, gl_stencil_op::type dppass)
	{
		SMAN_CHECK_STATE
		(
		if ( _sStencil.aSide[0].eSFail == sfail and _sStencil.aSide[0].eDFail == dfail and _sStencil.aSide[0].eDPPass == dppass and
			 _sStencil.aSide[1].eSFail == sfail and _sStencil.aSide[1].eDFail == dfail and _sStencil.aSide[1].eDPPass == dppass )
			return;
		);

		GL_CALL( glStencilOp( sfail, dfail, dppass ) );
		_sStencil.aSide[0].eSFail	= _sStencil.aSide[1].eSFail	 = sfail;
		_sStencil.aSide[0].eDFail	= _sStencil.aSide[1].eDFail	 = dfail;
		_sStencil.aSide[0].eDPPass	= _sStencil.aSide[1].eDPPass = dppass;
	}

	inline void GLStateManager::StencilOpSeparate(gl_face::type face, gl_stencil_op::type sfail, gl_stencil_op::type dfail, gl_stencil_op::type dppass)
	{
		const uint	i = face == gl_face::FRONT;

		SMAN_CHECK_STATE
		(
		if ( _sStencil.aSide[i].eSFail == sfail and _sStencil.aSide[i].eDFail == dfail and _sStencil.aSide[i].eDPPass == dppass )
			return;
		);

		GL_CALL( glStencilOpSeparate( face, sfail, dfail, dppass ) );
		_sStencil.aSide[i].eSFail	= sfail;
		_sStencil.aSide[i].eDFail	= dfail;
		_sStencil.aSide[i].eDPPass	= dppass;
	}

/*
=================================================
	изменяет режим отображение полигонов
=================================================
*/
	inline void GLStateManager::PolygonMode(gl_polygon_mode::type mode)
	{
		SMAN_CHECK_STATE
		(
		if ( _ePolygonMode == mode )
			return;
		);

		GL_CALL( glPolygonMode( GL_FRONT_AND_BACK, mode ) );
		_ePolygonMode = mode;
	}

/*
=================================================
	устанавливает программу
=================================================
*/
	inline void GLStateManager::UseProgram(GLuint uProgram)
	{
		SMAN_CHECK_STATE
		(
		if ( _bLockProgram or uProgram == _uProgram ) {
			GL_COUNTER( ++_cStatistic.uProgramRebind; );
			return;
		});
		GL_COUNTER( ++_cStatistic.uProgramBind; );

		GL_CALL( glUseProgram( uProgram ) );
		_uProgram = uProgram;
	}

/*
=================================================
	установить размеры поля вывода
=================================================
*/
	inline void GLStateManager::Viewport(const irect &sArea)
	{
		ASSERT( sArea.Width() != 0 and sArea.Height() != 0 );

		_sViewportStack.Get().sArea = sArea;
		ClearScissorStack();
		GL_CALL( glViewport( sArea.left, sArea.bottom, sArea.Width(), sArea.Height() ) );
	}
	
	inline void GLStateManager::Viewport(const ivec2 &vSize)
	{
		return Viewport( irect( ivec2(0), vSize ) );
	}
	
/*
=================================================
	вернуть предыдущий размер поля ввода
=================================================
*/
	inline void GLStateManager::PopViewport()
	{
		_sViewportStack.Pop();
		const irect	&s_area = _sViewportStack.Get().sArea;

		GL_CALL( glViewport( s_area.left, s_area.bottom, s_area.Width(), s_area.Height() ) );
		GL_CALL( glScissor(  s_area.left, s_area.bottom, s_area.Width(), s_area.Height() ) );
	}
	
/*
=================================================
	устанавливает область вырезки
=================================================
*/
	inline void GLStateManager::Scissor(const irect &sArea, bool bDepend)
	{
		if ( bDepend )
		{
			const irect	&s_prev	= _sViewportStack.Get().sScissorStack.Get();
			irect		s_new( sArea );

			s_new.left	 < s_prev.left	 ? s_new.left	= s_prev.left	: 0;
			s_new.bottom < s_prev.bottom ? s_new.bottom	= s_prev.bottom	: 0;
			s_new.right	 > s_prev.right	 ? s_new.right	= s_prev.right	: 0;
			s_new.top	 > s_prev.top	 ? s_new.top	= s_prev.top	: 0;

			_sViewportStack.Get().sScissorStack.Set( s_new );
		}
		else
			_sViewportStack.Get().sScissorStack.Set( sArea );

		const irect	&s_area = _sViewportStack.Get().sScissorStack.Get();

		ASSERT( s_area.Width() != 0 and s_area.Height() != 0 );
		GL_CALL( glScissor( s_area.left, s_area.bottom, s_area.Width(), s_area.Height() ) );
	}
	
/*
=================================================
	устанавливает область вырезки
=================================================
*/
	inline void GLStateManager::Scissor(const frect &sArea, bool bDepend)
	{
		const irect &s_view  = _sViewportStack.Get().sArea;
		const uint	u_width  = s_view.Width(),
					u_height = s_view.Height();

		Scissor( irect( s_view.left	  + int( u_width  * sArea.left	 + 0.5f ),
						s_view.bottom + int( u_height * sArea.bottom + 0.5f ),
						s_view.left	  + int( u_width  * sArea.right	 + 0.5f ),
						s_view.bottom + int( u_height * sArea.top	 + 0.5f ) ), bDepend );
	}
	
/*
=================================================
	очищает стек вырезки
=================================================
*/
	inline void GLStateManager::ClearScissorStack()
	{
		_sViewportStack.Get().sScissorStack.SetDefault( _sViewportStack.Get().sArea );
		_sViewportStack.Get().sScissorStack.Clear();
	}
	
/*
=================================================
	вернуть предыдущий размер поля вырезки
=================================================
*/
	inline void GLStateManager::PopScissor()
	{
		_sViewportStack.Get().sScissorStack.Pop();
		const irect	&s_area = _sViewportStack.Get().sScissorStack.Get();
		GL_CALL( glScissor( s_area.left, s_area.bottom, s_area.Width(), s_area.Height() ) );
	}
	
/*
=================================================
	сохранить текстуру
=================================================
*/
	inline bool GLStateManager::PushCurrentTexture()
	{
		return _sTexStack.Push( GLTexLayer( _aTexLayers[_uCurLayer].eTarget, _aTexLayers[_uCurLayer].uID ) );
	}
	
/*
=================================================
	установить предыдущую текстуру
=================================================
*/
	inline bool GLStateManager::PopCurrentTexture()
	{
		if ( _sTexStack.Pop() ) {
			BindTexture( _sTexStack.Get().eTarget, _sTexStack.Get().uID );
			return true;
		}
		return false;
	}
	
/*
=================================================
	сохранить фрэймбуфер
=================================================
*/
	inline bool GLStateManager::PushDrawFramebuffer()
	{
		return _sDrawFBOStack.Push( _uDrawFBO );
	}

	inline bool GLStateManager::PushReadFramebuffer()
	{
		return _sReadFBOStack.Push( _uReadFBO );
	}
	
/*
=================================================
	установить предыдущий фрэймбуфер
=================================================
*/
	inline bool GLStateManager::PopDrawFramebuffer()
	{
		if ( _sDrawFBOStack.Pop() ) {
			BindDrawFramebuffer( _sDrawFBOStack.Get() );
			return true;
		}
		return false;
	}

	inline bool GLStateManager::PopReadFramebuffer()
	{
		if ( _sReadFBOStack.Pop() ) {
			BindReadFramebuffer( _sReadFBOStack.Get() );
			return true;
		}
		return false;
	}
	
/*
=================================================
	сохранить массив вершин
=================================================
*/
	inline bool GLStateManager::PushVertexArray()
	{
		return _sVAOStack.Push( _uVAO );
	}
	
/*
=================================================
	установить предыдущий массив вершин
=================================================
*/
	inline bool GLStateManager::PopVertexArray()
	{
		if ( _sVAOStack.Pop() ) {
			BindVertexArray( _sVAOStack.Get() );
			return true;
		}
		return false;
	}

/*
=================================================
	добавляет состояние, возвращает номер
=================================================
*/
	inline e_gl_state::type GLStateManager::AddGLState(const TSMGLState &sState)
	{
		e_gl_state::type	ret = (e_gl_state::type)_aStates.Count();
		_aStates.PushBack( sState );
		return ret;
	}

/*
=================================================
	изменяет состояние
=================================================
*/
	inline void GLStateManager::SetGLState(const TSMGLState &sState, e_gl_state::type eType)
	{
		if ( (uint)eType >= _aStates.Count() )
			_aStates.Resize( eType +1, false );

		_aStates[eType] = sState;
	}
	
/*
=================================================
	отключает текстуру
=================================================
*/
	inline void GLStateManager::UnbindTexture(uint8 uStage)
	{
		BindMultiTexture( uStage, gl_texture::TEXTURE_2D, 0 );
	}
	
/*
=================================================
	отключает текстуру и сэмплер
=================================================
*/
	inline void GLStateManager::UnbindTexSampler(uint8 uStage)
	{
		BindMultiTexture( uStage, gl_texture::TEXTURE_2D, 0 );
		BindSampler( uStage, 0 );
	}

/*
=================================================
	включает/отключает тесселяцию и
	устанавливает размер патча
=================================================
*/
	inline void GLStateManager::EnableTessellation(bool bEnable, uint uPatchSize)
	{
		_bTessEnable = bEnable;

		if ( uPatchSize != 0 and _uPatchSize != uPatchSize ) {
			GL_CALL( glPatchParameteri( GL_PATCH_VERTICES, uPatchSize ) );
			_uPatchSize = uPatchSize;
		}
	}

/*
=================================================
	рисует примитивы, если включена
	тесселяция, то изменяет тип примитивов
=================================================
*/
	inline void GLStateManager::DrawArrays(gl_primitive::type mode, GLint first, GLsizei count)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawArrays( _bTessEnable ? gl_primitive::PATCH : mode, first, count ) );
	}


	inline void GLStateManager::DrawArraysIndirect(gl_primitive::type mode, const GLDrawArraysIndirectCommand * indirect)
	{
		GL_COUNTER( _cStatistic.uDrawCalls++; );
		GL_CALL( glDrawArraysIndirect( _bTessEnable ? gl_primitive::PATCH : mode, indirect ) );
	}


	inline void GLStateManager::DrawArraysInstanced(gl_primitive::type mode, GLint first, GLsizei count, GLsizei primcount)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count*primcount;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawArraysInstanced( _bTessEnable ? gl_primitive::PATCH : mode, first, count, primcount ) );
	}

	
	inline void GLStateManager::DrawArraysInstancedBaseInstance(gl_primitive::type mode, GLint first, GLsizei count, GLsizei primcount, GLuint baseinstance)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count*primcount;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawArraysInstancedBaseInstance( _bTessEnable ? gl_primitive::PATCH : mode, first, count, primcount, baseinstance ) );
	}


	inline void GLStateManager::DrawElements(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawElements( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices ) );
	}


	inline void GLStateManager::DrawElementsBaseVertex(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices, GLint basevertex)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawElementsBaseVertex( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices, basevertex ) );
	}


	inline void GLStateManager::DrawElementsIndirect(gl_primitive::type mode, gl_index::type type, const GLDrawElementsIndirectCommand * indirect)
	{
		GL_COUNTER( _cStatistic.uDrawCalls++; );
		GL_CALL( glDrawElementsIndirect( _bTessEnable ? gl_primitive::PATCH : mode, type, indirect ) );
	}


	inline void GLStateManager::DrawElementsInstanced(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices, GLsizei primcount)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count*primcount;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawElementsInstanced( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices, primcount ) );
	}

	
	inline void GLStateManager::DrawElementsInstancedBaseInstance(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid *indices, GLsizei primcount, GLuint baseinstance)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count*primcount;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawElementsInstancedBaseInstance( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices, primcount, baseinstance ) );
	}


	inline void GLStateManager::DrawElementsInstancedBaseVertex(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid * indices, GLsizei primcount, GLint basevertex)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count*primcount;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawElementsInstancedBaseVertex( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices, primcount, basevertex ) );
	}


	inline void GLStateManager::DrawElementsInstancedBaseVertexBaseInstance(gl_primitive::type mode, GLsizei count, gl_index::type type, const GLvoid *indices, GLsizei primcount, GLint basevertex, GLuint baseinstance)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count*primcount;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawElementsInstancedBaseVertexBaseInstance( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices, primcount, basevertex, baseinstance ) );
	}


	inline void GLStateManager::DrawRangeElements(gl_primitive::type mode, GLuint start, GLuint end, GLsizei count, gl_index::type type, const GLvoid * indices)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawRangeElements( _bTessEnable ? gl_primitive::PATCH : mode, start, end, count, type, indices ) );
	}


	inline void GLStateManager::DrawRangeElementsBaseVertex(gl_primitive::type mode, GLuint start, GLuint end, GLsizei count, gl_index::type type, const GLvoid * indices, GLint basevertex)
	{
		GL_COUNTER(
			_cStatistic.uPrimitiveCounter += count;
			_cStatistic.uDrawCalls++;
		);
		GL_CALL( glDrawRangeElementsBaseVertex( _bTessEnable ? gl_primitive::PATCH : mode, start, end, count, type, indices, basevertex ) );
	}


	inline void GLStateManager::DrawTransformFeedback(gl_primitive::type mode, GLuint id)
	{
		GL_CALL( glDrawTransformFeedback( _bTessEnable ? gl_primitive::PATCH : mode, id ) );
	}


	inline void GLStateManager::DrawTransformFeedbackInstanced(gl_primitive::type mode, GLuint id, GLsizei primcount)
	{
		GL_CALL( glDrawTransformFeedbackInstanced( _bTessEnable ? gl_primitive::PATCH : mode, id, primcount ) );
	}


	inline void GLStateManager::DrawTransformFeedbackStream(gl_primitive::type mode, GLuint id, GLuint stream)
	{
		GL_CALL( glDrawTransformFeedbackStream( _bTessEnable ? gl_primitive::PATCH : mode, id, stream ) );
	}


	inline void GLStateManager::DrawTransformFeedbackStreamInstanced(gl_primitive::type mode, GLuint id, GLuint stream, GLsizei primcount)
	{
		GL_CALL( glDrawTransformFeedbackStreamInstanced( _bTessEnable ? gl_primitive::PATCH : mode, id, stream, primcount ) );
	}


	inline void GLStateManager::MultiDrawArrays(gl_primitive::type mode, const GLint * first, const GLsizei * count, GLsizei primcount)
	{
		GL_COUNTER(
			_cStatistic.uDrawCalls++;
			for (GLsizei i = 0; i < primcount; ++i)
				_cStatistic.uPrimitiveCounter += count[i];
		);
		GL_CALL( glMultiDrawArrays( _bTessEnable ? gl_primitive::PATCH : mode, first, count, primcount ) );
	}


	inline void GLStateManager::MultiDrawElements(gl_primitive::type mode, const GLsizei * count, gl_index::type type, const GLvoid ** indices, GLsizei primcount)
	{
		GL_COUNTER(
			_cStatistic.uDrawCalls++;
			for (GLsizei i = 0; i < primcount; ++i)
				_cStatistic.uPrimitiveCounter += count[i];
		);
		GL_CALL( glMultiDrawElements( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices, primcount ) );
	}


	inline void GLStateManager::MultiDrawElementsBaseVertex(gl_primitive::type mode, const GLsizei * count, gl_index::type type, const GLvoid ** indices, GLsizei primcount, const GLint * basevertex)
	{
		GL_COUNTER(
			_cStatistic.uDrawCalls;
			for (GLsizei i = 0; i < primcount; ++i)
				_cStatistic.uPrimitiveCounter += count[i];
		);
		GL_CALL( glMultiDrawElementsBaseVertex( _bTessEnable ? gl_primitive::PATCH : mode, count, type, indices, primcount, basevertex ) );
	}
	
	inline void GLStateManager::MultiDrawArraysIndirect(gl_primitive::type mode, const GLDrawArraysIndirectCommand *indirect, GLsizei drawcount, GLsizei stride)
	{
		GL_COUNTER( _cStatistic.uDrawCalls++; );
		GL_CALL( glMultiDrawArraysIndirect( _bTessEnable ? gl_primitive::PATCH : mode, indirect, drawcount, stride ) );
	}

	inline void GLStateManager::MultiDrawElementsIndirect(gl_primitive::type mode, gl_index::type type, const GLDrawElementsIndirectCommand *indirect, GLsizei drawcount, GLsizei stride)
	{
		GL_COUNTER( _cStatistic.uDrawCalls++; );
		GL_CALL( glMultiDrawElementsIndirect( _bTessEnable ? gl_primitive::PATCH : mode, type, indirect, drawcount, stride ) );
	}

	
}	// GLShell

// Init Meta Data
UX_ENUM_META_OBJECT( GLShell::e_gl_state::type );

//-------------------------------------------------------------------

}	// UX_STL

#endif	// _UXS_GL_STATE_MANAGER_H